package at.jku.novellbedsidedevice.datasource.sleepquality;

import java.util.ArrayList;
import java.util.List;

import at.jku.novellbedsidedevice.datasource.AbstractDataSource;

public class SleepQualityDatasource extends AbstractDataSource<SleepQualityDataObject> {

	private static SleepQualityDatasource instance;

	public static SleepQualityDatasource getInstance() {
		if (instance == null) {
			instance = new SleepQualityDatasource();
		}
		return instance;
	}

	// list of current sensor data
	private List<SensorData> sensorDataList = new ArrayList<SensorData>();

	// list of all SleepQuality data objects
	private List<SleepQualityDataObject> sleepQualityDataList = new ArrayList<SleepQualityDataObject>();

	// windowsize of sliding window (in ms)
	private int windowSize = 10 * 1000; // 10 seconds

	// define accelarometer thresholds
	private float threshold = 0.05f;
	private float maximum = 0.5f;

	// last timestamp when sleepquality-dataobject was calculated
	private long lastCalculationTime = 0;

	private SleepQualityDatasource() {
	}

	// returns a list of all stored sleepquality data objects
	@Override
	public List<SleepQualityDataObject> getData() {
		return sleepQualityDataList;
	}

	// returns the latest (current) sleepquality data object
	public SleepQualityDataObject getLatestSleepQualityDataObject() {
		if (sleepQualityDataList.size() == 0)
			return null;
		return sleepQualityDataList.get(sleepQualityDataList.size()-1);
	}

	// returns a average sleepquality data object (of all stored sleepquality objects)
	public float getAverageSleepQualityInPercent() {
		if (sleepQualityDataList.size() == 0)
			return 0;
		
		float percentageSum = 0;
		for (SleepQualityDataObject sqdo : sleepQualityDataList) {
			percentageSum += sqdo.getQualityInPercent();
		}
		return percentageSum / sleepQualityDataList.size();
	}

	public void addAccelerometerData(SensorData sensorData) {

		// append new sensor data to list
		sensorDataList.add(sensorData);
		
		//System.out.println("data: "+Math.abs(sensorData.values[0]) + " " + Math.abs(sensorData.values[1]) + " " + Math.abs(sensorData.values[2]));


		// check last calculation timestamp to recalculate
		if (sensorData.timestamp - lastCalculationTime > windowSize) {

			lastCalculationTime = sensorData.timestamp;

			// calculate and add new sleepquality dataobject; also clears sensorDataList
			calculateCurrentSleepQuality();
		}
	}

	private void calculateCurrentSleepQuality() {

		float windowsum = 0;
		int windowamount = 0;
		float sum = 0;
		float quality = 0;

		// iterate through all received sensordata sets
		for (SensorData sensorData : sensorDataList) {
			
			// commulate absolute x,y,z accelerometer values
			sum = Math.abs(sensorData.values[0]) + Math.abs(sensorData.values[1]) + Math.abs(sensorData.values[2]);
			
			if (sum > threshold) {
				windowsum += sum - threshold;
				windowamount++;
			}
		}

		// calculate sleepquality (by maximum value) in percent (0.0 to 1.0)
		if (windowamount == 0) quality = 1;
		else {
			float avg = (windowsum / windowamount);
			if (avg < threshold) avg = 0;
			quality = 1 - (avg / (maximum - threshold));
			System.out.println("windowsum= "+windowsum+" windowamount= "+windowamount + "avg= "+avg);
		}
		
		if (quality < 0) quality = 0;

		// create new SleepQualityDataObject and add to list
		long timestamp = sensorDataList.get(0).timestamp; // timestamp of first element
		SleepQualityDataObject sleepQualityDataObject = new SleepQualityDataObject(timestamp, quality);
		sleepQualityDataList.add(sleepQualityDataObject);
		
		System.out.println("SleepQualityDatasource: new calculated sleep quality = " + sleepQualityDataObject.getQualityInPercent() + "%");

		// remove all stored sensor data
		sensorDataList.clear();
	}
	
	public void flushStoredSleepQualityObjects() {
		instance = null;
	}
}
